package se581.banker.service;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import se581.banker.listener.RegistrationListener;
import se581.banker.manager.TrackerObserver;
import se581.banker.util.ServerSocketInfo;

/**
 * This class manages client registration for Update and Inquiry servers.
 * It also manages server instances, server health and fail over.
 * 
 * @author mmichalak
 *
 */
public class RegistrationService {
	static Logger logger = Logger.getLogger(RegistrationService.class.getName());
	
	RegistrationListener regListener = null;
	
	List<ServerSocketInfo> updateTrackers = null;	
	List<ServerSocketInfo> inquiryTrackers = null;
	// Used to keep track of which inst gets registered for next user
	int nextUpdateSvr = 0;
	int nextInquirySvr = 0;
	
	// Observers to get new AccountTracker upon failover.  Currently not Implemented
	ArrayList<TrackerObserver> aTrackerObs = new ArrayList<TrackerObserver>();
	ArrayList<TrackerObserver> bTrackerObs = new ArrayList<TrackerObserver>();
	
	HealthMonitorService healthMonitor = null;
	
	
	
	
	/**
	 * This performs a failover.
	 * Currently only UpdateTrackers are fail-safe.  This method is notified by the HealthService of a
	 * failure and removed from service (i.e. taken out of the updateTracker List
	 * 
	 * @param ServerSocketInfo for failed UpdateTracker
	 */
	public synchronized void serverFailure(ServerSocketInfo failedServerConnInfo) {
		
		int indexOfRemovedSvr = removeUpdateTrackerInfo(failedServerConnInfo);
		if (indexOfRemovedSvr >= 0) 
			logger.error("----------------------> UpdateTracker " + failedServerConnInfo + " is successfully removed from service.");
		else
			logger.error("----------------------> UpdateTracker " + failedServerConnInfo + " is NOT successfully removed from service.  Something not working correctly...");
		
		
		indexOfRemovedSvr = removeInquiryTrackerInfo(indexOfRemovedSvr);
		if (indexOfRemovedSvr >= 0) 
			logger.error("----------------------> InquiryTracker " + failedServerConnInfo + " is successfully removed from service.");
		else
			logger.error("----------------------> InquiryTracker " + failedServerConnInfo + " is NOT successfully removed from service.  Something not working correctly...");
		
		

/*		OLD code from Observer pattern on failover.  Prob won't need or use....
 
		StringBuilder sb = new StringBuilder();
		sb.append("FAILURE OF PROCESSOR ").append(primaryProc.getId()).append(" at timestamp "); //.append(ev.getTick());
		logger.debug(sb.toString());
		StaticOutputFileWriterUtil.writeToFile(sb.toString());
		
		Processor failedProc = primaryProc;
		primaryProc = backupProc;
		
		AccountTracker p1TrackA = new UpdateTracker();
		failedProc.setUdpateTracker(p1TrackA);
		AccountTracker p1TrackB = new InquiryTracker();
		failedProc.setInquiryTracker(p1TrackB);
		backupProc = failedProc;
		
		 // Push outstanding partials into backup's datastore
		SynchableTracker a = (SynchableTracker)primaryProc.getUdpateTracker();
		
		// NOTIFY AccountTracker Observers of the failure and provide new AccountTracker obj
		for (TrackerObserver obsa : aTrackerObs) {
			obsa.updateTracker(primaryProc.getUdpateTracker());
		}
		for (TrackerObserver obsb : bTrackerObs) {
			obsb.updateTracker(primaryProc.getInquiryTracker());
		}
*/	}

	
	public synchronized boolean registerServer(ServerSocketInfo updateServer, ServerSocketInfo queryServer) {
		
		// LEAVE to check/verfihy server already registered   
		// int indexOfRemovedSvr = removeUpdateTrackerInfo(newServerConnInfo);
		//		if (indexOfRemovedSvr >= 0) 
		//			logger.error("----------------------> UpdateTracker " + failedServerPort + " is successfully removed from service.");
		// return false
		updateTrackers.add(updateServer);
		inquiryTrackers.add(queryServer);
		
		healthMonitor.startNewHealthListener(updateServer);
		
		return true;
	}

	
	
	/**
	 * Helper methods to add registered UpdateTrackers and InquiryTrackers
	 * 
	 * @param serverSocketInfo
	 */
	public void addUpdateTrackerInfo(ServerSocketInfo serverSocketInfo) {
		updateTrackers.add(serverSocketInfo);
	}
	
	public void addInquiryTrackerInfo(ServerSocketInfo serverSocketInfo) {
		inquiryTrackers.add(serverSocketInfo);
	}
	
	/**
	 * 
	 * @param serverToRemove - index of Inquire server to remove based on the
	 * index of the failed UpdateServer
	 * 
	 * SEE COMMENTS FOR removeUpdateTrackerInfo() below
	 * 
	 * @return - index of the server removed
	 */
	public int removeInquiryTrackerInfo(int serverToRemoveIndex) {
		
		ServerSocketInfo removedServer = inquiryTrackers.remove(serverToRemoveIndex);
		
		if (removedServer != null)
			return serverToRemoveIndex;
		else
			return -1; // Will signify error
	}
	
	/**
	 * 
	 * @param serverToRemove
	 * 
	 * @return index in Update List of removed server.  This is used by
	 * the InquiryServer to remove it.  I.e. if the UpdateTracker is down,
	 * the Inquiry server on that host is also removed from service
	 * THIS IS HACKY... (relying on the index...)
	 */
	public int removeUpdateTrackerInfo(ServerSocketInfo serverToRemove) {
		
		int serverToRemoveIndex = updateTrackers.indexOf(serverToRemove);
		ServerSocketInfo removedServer = updateTrackers.remove(serverToRemoveIndex);
		
		if (removedServer != null)
			return serverToRemoveIndex;
		else
			return -1; // Will signify error
	}

	/**
	 * Round-robin selection of an update server
	 * 
	 * @param serverPort
	 * @return
	 */
	public ServerSocketInfo returnNextUpdateTracker() {
		
		if (updateTrackers.size() > nextUpdateSvr) {
			return updateTrackers.get(nextUpdateSvr++);
		}
		else if (updateTrackers.size() <= nextUpdateSvr) {
			nextUpdateSvr = 0; // reset back to first svr
			return updateTrackers.get(nextUpdateSvr);
		}
		else {
			logger.error("next svr index is off/misaligned with updateTrackers List");
			return null; // should never get here
		}
	}


	/**
	 * Round-robin selection of an inquiry server
	 * 
	 * @param serverPort
	 * @return
	 */
	public ServerSocketInfo returnNextInquiryTracker() {

		if (inquiryTrackers.size() > nextInquirySvr) {
			return inquiryTrackers.get(nextInquirySvr++);
		}
		else if (inquiryTrackers.size() <= nextInquirySvr) {
			nextInquirySvr = 0; // reset back to first svr
			return inquiryTrackers.get(nextInquirySvr);
		}
		else {
			logger.error("next svr index is off/misaligned with inquiryTrackers List");
			return null; // should never get here
		}
	}
	
	
	public void setUpdateTrackers(List<ServerSocketInfo> updateTrackers) {
		this.updateTrackers = updateTrackers;
	}



	public void setInquiryTrackers(List<ServerSocketInfo> inquiryTrackers) {
		this.inquiryTrackers = inquiryTrackers;
	}


	public List<ServerSocketInfo> getUpdateTrackers() {
		return updateTrackers;
	}


	public HealthMonitorService getHealthMonitor() {
		return healthMonitor;
	}


	public void setHealthMonitor(HealthMonitorService healthMonitor) {
		this.healthMonitor = healthMonitor;
	}


	public RegistrationListener getRegListener() {
		return regListener;
	}


	public void setRegListener(RegistrationListener regListener) {
		this.regListener = regListener;
	}
}
